package com.hw.backapi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.amazonaws.services.s3.model.S3Object;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hw.backapi.context.AwsOssContext;
import com.hw.backapi.controller.PatientController;
import com.hw.backapi.service.IMemberService;
import com.hw.backapi.service.IOssMemoService;
import com.hw.backapi.service.IOssService;
import com.hw.common.contants.PageInfoContants;
import com.hw.common.enums.GenderStatusEnum;
import com.hw.common.exception.BaseException;
import com.hw.common.utils.DateUtils;
import com.hw.common.utils.ExportUtil;
import com.hw.common.utils.MomoUtil;
import com.hw.common.utils.ServletUtil;
import com.hw.model.common.PageInfo;
import com.hw.model.dto.overview.DetailedQueryDTO;
import com.hw.model.dto.overview.PatientDetailedQueryDTO;
import com.hw.model.entity.MemberPO;
import com.hw.model.entity.OssMemoPO;
import com.hw.model.entity.OssPO;
import com.hw.model.mapper.ClientMapper;
import com.hw.model.mapper.OssMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hw.model.vo.doctor.DoctorBaseVO;
import com.hw.model.vo.overview.DataDetailVO;
import com.hw.model.vo.overview.OssInfoVO;
import com.hw.model.vo.overview.PatientDataDetailExportVO;
import com.hw.model.vo.overview.PatientDataDetailVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 作者
 * @since 2022-11-24
 */
@Service
public class OssServiceImpl extends ServiceImpl<OssMapper, OssPO> implements IOssService {

    @Autowired
    AwsOssContext awsOssContext;

    @Autowired
    IOssMemoService ossMemoServiceImpl;

    @Autowired
    IMemberService memberServiceImpl;

    @Resource
    ClientMapper clientMapper;

    @Override
    public PageInfo<PatientDataDetailVO> patientDetailed(PatientDetailedQueryDTO patientDetailedQueryDTO) {
        if (patientDetailedQueryDTO == null) {
            patientDetailedQueryDTO = new PatientDetailedQueryDTO();
        }
        if (null == patientDetailedQueryDTO.getPageNum() || patientDetailedQueryDTO.getPageNum() < 1) {
            patientDetailedQueryDTO.setPageNum(PageInfoContants.PAGE_NUM);
            patientDetailedQueryDTO.setPageSize(PageInfoContants.PAGE_SIZE);
        }

        if (!StrUtil.isEmpty(patientDetailedQueryDTO.getShotDateBegin())) {
            patientDetailedQueryDTO.setShotDateBeginSql(Timestamp.valueOf(patientDetailedQueryDTO.getShotDateBegin() + DateUtils.TIME_BEGIN_SUFFIX));
            patientDetailedQueryDTO.setShotDateEndSql(Timestamp.valueOf(patientDetailedQueryDTO.getShotDateEnd() + DateUtils.TIME_END_SUFFIX));
        }

        PageInfo<PatientDataDetailVO> res = new PageInfo<>(patientDetailedQueryDTO.getPageNum(),
                patientDetailedQueryDTO.getPageSize());

        //通过名称查询用户id
        if (StrUtil.isNotEmpty(patientDetailedQueryDTO.getDoctorName())) {
            List<DoctorBaseVO> doctorList =
                    clientMapper.getDoctorListByName(patientDetailedQueryDTO.getDoctorName());
            if (CollectionUtil.isNotEmpty(doctorList)) {
                patientDetailedQueryDTO.setDoctorIds(
                        doctorList.stream().map(DoctorBaseVO::getId).collect(Collectors.toList())
                );
            } else {
                res.setList(Collections.emptyList());
                return res;
            }
        }


        Page<PatientDataDetailVO> page = new Page<>(patientDetailedQueryDTO.getPageNum(),
                patientDetailedQueryDTO.getPageSize());
        Page<PatientDataDetailVO> dataDetailVOPage = this.baseMapper.patientDataDetail(page, patientDetailedQueryDTO);

        List<PatientDataDetailVO> patientDataDetailVOList = setOssInfo(dataDetailVOPage.getRecords());

        patientDataDetailVOList.forEach(v -> {
            MemberPO memberInfo = memberServiceImpl.getMemberInfo(v.getFid(), v.getClientId());
            if (null != memberInfo) {
                v.setDoctorName(memberInfo.getHonorific());
            } else {
                v.setDoctorName(StrUtil.EMPTY);
            }

            v.setFilenamePress(v.getFilename() + ".csv");
        });

        res.setPage(dataDetailVOPage.getTotal(), patientDataDetailVOList);

        return res;
    }

    @Override
    public void exportOss(Long ossId, Long exportType, HttpServletResponse response) {
        if (null == ossId) {
            throw new BaseException("The OSS file id is empty");
        }
        OssPO ossPO = this.baseMapper.selectById(ossId);
        if (null == ossPO) {
            throw new BaseException("The OSS file does not exist");
        }

        if (exportType == 1) {
            ossPO.setKey(ossPO.getKey() + ".csv");
            ossPO.setFilename(ossPO.getFilename() + ".csv");
            ossPO.setDiagKey(ossPO.getDiagKey() + ".csv");
        }

        String fileName = getOssFileName(ossPO.getIsVideo(), ossPO.getKey(), ossPO.getDiagKey());
        //下载
        awsOssContext.downloadFile(fileName, ossPO.getFilename(), response);
    }

    @Override
    public String getOssFileName(Integer type, String key, String diagKey) {
        String fileName = "public/" + key;
        if (type != null && type.equals(1)) {
            fileName = "public/" + diagKey;
        }
        return fileName;
    }

    @Override
    public void batchDownload(PatientDetailedQueryDTO patientDetailedQueryDTO, HttpServletRequest request,
                              HttpServletResponse response) {
        if (patientDetailedQueryDTO == null) {
            patientDetailedQueryDTO = new PatientDetailedQueryDTO();
        }
        patientDetailedQueryDTO.setPageNum(1);
        patientDetailedQueryDTO.setPageSize(PageInfoContants.PAGE_SIZE_ALL);

        PageInfo<PatientDataDetailVO> detailed = this.patientDetailed(patientDetailedQueryDTO);
        List<PatientDataDetailVO> fileList = detailed.getList();
        try {
            // 初始化
            //zip的名称
            String fileName = "batchBonesCsv.zip";
            // 创建临时文件
            File zipFile = File.createTempFile("batchDownload", ".zip");
            FileOutputStream f = new FileOutputStream(zipFile);
            /**
             * 作用是为任何OutputStream产生校验和
             * 第一个参数是制定产生校验和的输出流，第二个参数是指定Checksum的类型 （Adler32（较快）和CRC32两种）
             */
            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            // 用于将数据压缩成Zip文件格式
            ZipOutputStream zos = new ZipOutputStream(csum);
            //域名
            for (PatientDataDetailVO dataDetailVO : fileList) {
                //得到要下载的文件名（Object Name）字符串
                String ossfile = getOssFileName(1, dataDetailVO.getOssKey(),
                        dataDetailVO.getOssDiagKey());
                String filenameEntry = dataDetailVO.getFilename();
                for (int i = 0; i < 2; i++) {
                    //如果是0，但是未上传mp4，直接跳过
                    if( i== 0 && !dataDetailVO.getVideoUploaded().equals(1) ){
                        continue;
                    }
                    if (i == 1) {
                        ossfile = ossfile + ".csv";
                        filenameEntry = filenameEntry + ".csv";
                    }
                    // 获取Object，返回结果为OSSObject对象
                    S3Object ossObject = awsOssContext.getOssClient().getObject(awsOssContext.getOssBucketName(),
                            ossfile);
                    // 读去Object内容  返回
                    InputStream inputStream = ossObject.getObjectContent();
                    // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名

                    zos.putNextEntry(new ZipEntry(filenameEntry));
                    int bytesRead = 0;
                    // 向压缩文件中输出数据
                    while ((bytesRead = inputStream.read()) != -1) {
                        zos.write(bytesRead);
                    }
                    inputStream.close();
                    zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
                }

            }
            zos.close();
            String header = request.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "utf-8");
                fileName = fileName.replace("+", "%20");    //IE下载文件名空格变+号问题
            } else {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            }
            response.reset();
            response.setContentType("text/plain");
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", fileName);
            response.setHeader("Cache-Control", "max-age=0");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

            FileInputStream fis = new FileInputStream(zipFile);
            BufferedInputStream buff = new BufferedInputStream(fis);
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];
            int l = 0;
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }
            // 关闭流
            fis.close();
            buff.close();
            out.close();


            // 删除临时文件
            zipFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void exportDetailed(PatientDetailedQueryDTO patientDetailedQueryDTO) {
        if (patientDetailedQueryDTO == null) {
            patientDetailedQueryDTO = new PatientDetailedQueryDTO();
        }
        patientDetailedQueryDTO.setPageNum(1);
        patientDetailedQueryDTO.setPageSize(PageInfoContants.PAGE_SIZE_ALL);

        PageInfo<PatientDataDetailVO> detailed = this.patientDetailed(patientDetailedQueryDTO);


        //获取memo
        List<Long> cids =
                detailed.getList().stream().map(PatientDataDetailVO::getCid).collect(Collectors.toList());
        List<OssMemoPO> ossMemoList = ossMemoServiceImpl.getOssMemoList(cids);
        //患者疾病分组:患者id分组
        Map<Long, List<OssMemoPO>> ossMemoOssIdList =
                ossMemoList.stream().collect(Collectors.groupingBy(OssMemoPO::getOssId));

        //通过ossid分组
        Map<Long, OssMemoPO> ossMemoOssIdMap = ossMemoList.stream().collect(Collectors.toMap(OssMemoPO::getOssId,
                Function.identity(), (a, b) -> a));

        List<PatientDataDetailExportVO> exportData = detailed.getList().stream().map(patientDataDetailVO -> {

            PatientDataDetailExportVO patientDataDetailExportVO = BeanUtil.toBean(patientDataDetailVO,
                    PatientDataDetailExportVO.class);

            //性别：1男2女
            patientDataDetailExportVO.setGenderName(GenderStatusEnum.getByCode(patientDataDetailExportVO.getGender()).getName());

            //计算拍摄时年龄
            patientDataDetailExportVO.setAge(DateUtils.getAge(patientDataDetailVO.getOssCreatedAt(),
                    patientDataDetailVO.getBirthday()));

            //oss对应的memo和疾病名称
            OssMemoPO ossMemoPO = ossMemoOssIdMap.get(patientDataDetailVO.getOssId());
            if (null != ossMemoPO) {
                patientDataDetailExportVO.setMemo(getMemo(ossMemoPO));
                patientDataDetailExportVO.setDiseaseName(ossMemoPO.getDiseaseName());
            }

            //患者memo列表 如果不为空。则处理memo
            List<OssMemoPO> patientOssMemoList = ossMemoOssIdList.get(patientDataDetailVO.getOssId());
            if (CollectionUtil.isNotEmpty(patientOssMemoList)) {
                List<String> memoExportList = new ArrayList<>();


                //memolist
                patientOssMemoList.stream()
                        .filter(v -> !v.getId().equals(ossMemoPO.getId()))
                        .forEach(ossMemo -> {

                            //memo信息
                            memoExportList.add(getMemo(ossMemo));

                        });

                if( CollectionUtil.isNotEmpty(memoExportList) ) {
                    //备忘录历史
                    String memoHistory = CollectionUtil.join(memoExportList, "----").replace("----",
                            String.valueOf((char) 10));
                    patientDataDetailExportVO.setMemoHistory(memoHistory);
                }
            }

            patientDataDetailExportVO.setCreatedAt(DateUtils.convertToLocal(patientDataDetailExportVO.getCreatedAt()));

            return patientDataDetailExportVO;
        }).collect(Collectors.toList());


        //获取响应体
        HttpServletResponse response = ServletUtil.getResponse();

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = UUID.randomUUID().toString() + ".xlsx";
        response.setHeader("Content-disposition", "attachment;filename*=" + fileName);

//        com.alibaba.excel.util.DateUtils.setDefaultTimeZone(TimeZone.getTimeZone("JST"));

        try {
            EasyExcel.write(response.getOutputStream(), PatientDataDetailExportVO.class)
                    .sheet("detail")
                    .doWrite(() -> exportData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String getMemo(OssMemoPO ossMemoPO) {
        String whenWalkingStr = StrUtil.EMPTY;
        String affectSideStr = StrUtil.EMPTY;
        List<String> positionStr = new ArrayList();
        List<String> gaitStr = new ArrayList();
        String diseaseName = StrUtil.EMPTY;
        String custom = StrUtil.EMPTY;
        String doctor = StrUtil.EMPTY;
        String date = DateUtils.formatDate(ossMemoPO.getCreatedAt());

        if (null != ossMemoPO.getMoodWhenWalking()) {
            whenWalkingStr = MomoUtil.memoMoodWhenWalking.get(ossMemoPO.getMoodWhenWalking().toString());
        }
        if (null != ossMemoPO.getAffectedSide()) {
            affectSideStr = MomoUtil.memoAffectSide.get(ossMemoPO.getAffectedSide().toString());
        }
        if (null != ossMemoPO.getPosition()) {
            String[] positions = ossMemoPO.getPosition().split(",");
            for (String position : positions) {
                String positionVal = MomoUtil.memoPosition.get(position);
                if ("Others".equals(positionVal)) {
                    positionStr.add(ossMemoPO.getPositionOther());
                } else {
                    positionStr.add(positionVal);
                }
            }
        }
        if (null != ossMemoPO.getGait()) {
            String[] gaits = ossMemoPO.getGait().split(",");
            for (String gait : gaits) {
                String gaitVal = MomoUtil.memoGait.get(gait);
                if ("Others".equals(gaitVal)) {
                    gaitStr.add(ossMemoPO.getGaintOther());
                } else {
                    gaitStr.add(gaitVal);
                }
            }
        }
        if( StrUtil.isNotEmpty(ossMemoPO.getDiseaseName()) ){
            diseaseName = ossMemoPO.getDiseaseName();
        }
        if( StrUtil.isNotEmpty(ossMemoPO.getCustom()) ){
            custom = ossMemoPO.getCustom();
        }

        //医生名称
        MemberPO memberInfo = memberServiceImpl.getMemberInfo(ossMemoPO.getFid(), ossMemoPO.getClientId());
        if( null != memberInfo ){
            doctor = memberInfo.getHonorific();
        }

        return String.format(
                "Doctor: %s ;Date: %s ; Disease name: %s ; Custom memo message: %s ; Mood when walking: %s ; Affected side: %s ; Position : %s ; Gait: %s",
                doctor,
                date,
                diseaseName,
                custom,
                whenWalkingStr,
                affectSideStr,
                String.join(",", positionStr),
                String.join(",", gaitStr)
        );
    }

    /**
     * @param list
     * @return java.util.List<com.hw.model.vo.overview.PatientDataDetailVO>
     * @descrition 设置ossInfo
     * @author lipeng
     * @date 2023/3/9
     */
    private List<PatientDataDetailVO> setOssInfo(List<PatientDataDetailVO> list) {
        list.forEach(patientDataDetailVO -> {
            if( patientDataDetailVO.getVideoUploaded().equals(1) ) {
                OssInfoVO ossInfoVO = new OssInfoVO();
                ossInfoVO.setOssId(patientDataDetailVO.getOssId());
                ossInfoVO.setKey(patientDataDetailVO.getOssKey());
                ossInfoVO.setFilename(patientDataDetailVO.getFilename());
                ossInfoVO.setIsVideo(1L);

                patientDataDetailVO.setVideo(ossInfoVO);
            }


            OssInfoVO ossInfoVOCsv = new OssInfoVO();
            ossInfoVOCsv.setOssId(patientDataDetailVO.getOssId());
            ossInfoVOCsv.setKey(patientDataDetailVO.getOssKey());
            ossInfoVOCsv.setIsVideo(0L);
            ossInfoVOCsv.setFilename(patientDataDetailVO.getFilename() + ".csv");
            patientDataDetailVO.setCsv(ossInfoVOCsv);

//            List<OssInfoVO> ossInfoByFilename =
//                    this.baseMapper.getOssInfoByFilename(patientDataDetailVO.getFilenamePress());
//            if( CollectionUtil.isNotEmpty(ossInfoByFilename) ){
//                ossInfoByFilename.forEach(ossInfoVO -> {
//                    if( ossInfoVO.getIsVideo() == 1 ){
//                        patientDataDetailVO.setVideo(ossInfoVO);
//                    }else{
//                        patientDataDetailVO.setCsv(ossInfoVO);
//                    }
//                });
//            }
        });
        return list;
    }


}
